use crate::config::{CONTEXT, INS_DIR_NAME, PKG_FILE_NAME};
use crate::global::{DpmCell, DpmError, PkgInfo};
use rcmd_core::chrono::format;
use rcmd_core::log::{self, *};
use regex::Regex;
use serde_json::json;
use std::collections::HashMap;
use std::env;
use std::io::{Read, Write};
use std::path::Path;
use yaml_rust::{Yaml, YamlLoader};

pub mod add_runtime;
mod basecmd;
pub mod init;
pub mod install;
pub mod list;
pub mod publish;

macro_rules! yaml_string {
    ($a: expr) => {
        if (!$a.is_badvalue()) {
            Some(String::from($a.as_str().unwrap_or_default()))
        } else {
            Some("".to_string())
        }
    };
}

macro_rules! yaml_int {
    ($a: expr) => {
        if (!$a.is_badvalue()) {
            Some($a.as_i64().unwrap() as u16)
        } else {
            Some(0 as u16)
        }
    };
}

//PKG Require配置字段
enum EPkgRequireKeys {
    Name,
    Ver,
    License,
}

//PKG Option配置字段
enum EPkgOptionsKeys {
    Script,
}

///获取要执行命令的理路径
pub fn get_exec_path() -> String {
    let cd = env::current_dir().unwrap();
    let cur = cd.to_str().unwrap();

    let ret = env::var("DPM_WORKPATH").unwrap_or(cur.to_owned());
    return ret;
}

// =========================ymlconf-file=========================

/// 加载 PKG_FILE_NAME yml文件
pub fn parse_ymlconf(conf: &str) -> Result<Yaml, DpmError> {
    let mut file = std::fs::File::open(conf)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    drop(file);
    let docs = YamlLoader::load_from_str(contents.as_str()).unwrap();
    let doc = &docs[0];
    Ok(doc.to_owned())
}

/// 自检路径配置是否初始化
pub fn is_ymlconf(path: &str) -> bool {
    let f = String::from(path);
    let tar = Path::new(&f).join(PKG_FILE_NAME);
    let is_m = tar.metadata().is_err();
    if !is_m {
        return tar.metadata().unwrap().is_file();
    }
    return false;
}

/// 初始化当前路径
pub fn lazy_init_ymlconf(path: &str) -> Result<(), DpmError> {
    let tar = Path::new(&path).join(PKG_FILE_NAME);
    let v = path
        .split(std::path::MAIN_SEPARATOR)
        .map(|s| s.to_string())
        .collect::<Vec<String>>();
    let dir_n = v.get(v.len() - 1).unwrap();
    let f = tar.to_str().unwrap();
    return match std::fs::File::create(f) {
        Ok(mut v) => {
            match v.write_all(
                include_str!("../static/dpm.yml")
                    .replace("$nickname", dir_n)
                    .as_bytes(),
            ) {
                Ok(_) => {
                    info!("Init success!");
                    Ok(())
                }
                Err(v) => Err(DpmError::IoError(v)),
            }
        }
        Err(v) => Err(DpmError::IoError(v)),
    };
}

// =========================install-path=========================
/// 自检package安装路径
pub fn chk_pkginstall_path(path: &str) -> std::path::PathBuf {
    let ins_p = Path::new(path).join(INS_DIR_NAME);
    if !ins_p.is_dir() {
        let _ = std::fs::create_dir(&ins_p);
    }
    ins_p
}

///遍历安装路径
/// 查看所有安装的packages
pub fn walk_pkginstall_path(path: &str) -> Result<Vec<DpmCell>, std::io::Error> {
    let mut ret: Vec<DpmCell> = vec![];
    let ins_path = Path::new(path).join(INS_DIR_NAME);
    if ins_path.is_dir() {
        for entry in std::fs::read_dir(ins_path)? {
            let path = entry?.path();
            log::debug!("check entry of {:?}", path);
            if std::fs::metadata(&path)?.is_dir() {
                let conf = path.to_owned().as_path().join(PKG_FILE_NAME);
                let modal = parse_check_ymlconf(&conf.display().to_string());
                if let Ok(m) = modal {
                    ret.push(m.base);
                } else {
                    warn!(
                        "walk package installed path in <{}>, but load config failed!",
                        &path.display()
                    );
                }
            }
        }
    }
    Ok(ret)
}

///检查PKG配置的值得有效性(参照:mysql 表字段)
fn chk_ymlconf_val(key: EPkgRequireKeys, val: Option<String>) -> Option<String> {
    if val.is_none() {
        return val;
    }
    match key {
        EPkgRequireKeys::Name => {
            if val.as_ref().unwrap().len() > 64 {
                None
            } else {
                val
            }
        }
        EPkgRequireKeys::Ver => {
            let reg = Regex::new(r#"\d{1,2}\.\d{1,2}\.\d{1,2}"#).unwrap();
            let ret: Vec<&str> = reg
                .find_iter(val.as_ref().unwrap().as_str())
                .map(|mat| mat.as_str())
                .collect();
            if ret.len() > 0 {
                Some(ret[0].to_string())
            } else {
                Some("".to_string())
            }
        }
        _ => Some(val.unwrap().to_string()),
    }
}

/// 读取当前配置获取DpmInfo
fn parse_check_ymlconf(pkg_f: &str) -> Result<PkgInfo, DpmError> {
    let o: Yaml = parse_ymlconf(pkg_f)?;
    // only use inner package self
    let mut yaml_script: HashMap<String, String> = HashMap::new();

    if let Some(linkmap) = o["scripts"].as_hash() {
        for k in linkmap.keys() {
            let k_str = k.as_str().unwrap_or_default().to_string();
            if k_str.len() > 0 {
                if let Some(v) = linkmap.get(k) {
                    yaml_script.insert(k_str, v.as_str().unwrap_or_default().to_owned());
                }
            }
        }
    };
    Ok(PkgInfo {
        base: DpmCell {
            name: chk_ymlconf_val(EPkgRequireKeys::Name, yaml_string!(o["name"])).unwrap(),
            ver: chk_ymlconf_val(EPkgRequireKeys::Ver, yaml_string!(o["version"])).unwrap(),
            descript: yaml_string!(o["descript"]),
            license: yaml_string!(o["license"]),
            lan_id: yaml_int!(o["lan_id"]),
            md5: None,
            runtime: yaml_string!(o["runtime"]),
            url: Some(CONTEXT.uri_open.to_owned()),
            scripts: Some(yaml_script),
        },
    })
}

/// 获取当前文件夹配置的配置内容
pub fn ymlconf_to_model(path: &str) -> Result<PkgInfo, DpmError> {
    let is = is_ymlconf(path);
    if !is {
        return Err(DpmError::NotInitYet);
    }
    let pkg_f = Path::join(path.as_ref(), PKG_FILE_NAME);
    let yml_info = parse_check_ymlconf(pkg_f.to_str().unwrap())?;
    if yml_info.base.name.len() < 3 {
        return Err(DpmError::ApiFailed(
            "Package's name len could not more than 10".to_string(),
        ));
    }
    Ok(yml_info)
}

// =========================oss about=========================
// 获取oss文件存储的相对路径
pub fn oss_relpath(info: &DpmCell) -> String {
    return format!("plugins/{pm}@{ver}.zip", pm = info.name, ver = info.ver).to_owned();
}


#[test]
fn test_walk() {
    use std::env;
    let cur_p = env::current_dir().unwrap();
    let c = walk_pkginstall_path(&cur_p.display().to_string());
    println!("target path is {:?}, \n result c is {:?}", cur_p, c);
}
